View Javadoc

1   /*
2    * Copyright (C) 2004 TiongHiang Lee
3    *
4    * This library is free software; you can redistribute it and/or
5    * modify it under the terms of the GNU Lesser General Public
6    * License as published by the Free Software Foundation; either
7    * version 2.1 of the License, or (at your option) any later version.
8    *
9    * This library is distributed in the hope that it will be useful,
10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12   * Lesser General Public License for more details.
13   *
14   * You should have received a copy of the GNU Lesser General Public
15   * License along with this library; if not,  write to the Free Software
16   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17   *
18   * Email: thlee@onemindsoft.org
19   */
20  
21  package org.onemind.commons.java.lang.ref;
22  
23  import java.util.*;
24  import java.lang.ref.*;
25  /***
26   * @author TiongHiang Lee (thlee@onemindsoft.org)
27   * @version $Id: SoftHashMap.java,v 1.1 2004/10/23 15:24:35 thlee Exp $ $Name:  $
28   * Credits: Article by Heinz Kabutz at http://archive.devx.com/java/free/articles/Kabutz01/Kabutz01-1.asp
29   */
30  public class SoftHashMap extends AbstractMap
31  {
32  
33      /*** We define our own subclass of SoftReference which contains
34       not only the value but also the key to make it easier to find
35       the entry in the HashMap after it's been garbage collected. */
36      private static class SoftValue extends SoftReference
37      {
38          private final Object key; // always make data member final
39  
40          /*** Did you know that an outer class can access private data
41           members and methods of an inner class?  I didn't know that!
42           I thought it was only the inner class who could access the
43           outer class's private information.  An outer class can also
44           access private members of an inner class inside its inner
45           class. */
46          private SoftValue(Object k, Object key, ReferenceQueue q)
47          {
48              super(k, q);
49              this.key = key;
50          }
51      }
52  
53      /*** The internal HashMap that will hold the SoftReference. */
54      private final Map hash = new HashMap();
55  
56      /*** The number of "hard" references to hold internally. */
57      private final int HARD_REF_SIZE;
58  
59      /*** The FIFO list of hard references, order of last access. */
60      private final LinkedList hardRefCache;
61  
62      /*** Reference queue for cleared SoftReference objects. */
63      private final ReferenceQueue queue = new ReferenceQueue();
64  
65      /***
66       * Constructor     
67       */
68      public SoftHashMap()
69      {
70          this(0);
71      }
72  
73      /***
74       * Constructor
75       * @param hardSize the hard reference size to maintain
76       */
77      public SoftHashMap(int hardSize)
78      {
79          HARD_REF_SIZE = hardSize;
80          if (HARD_REF_SIZE>0)
81          {
82              hardRefCache = new LinkedList();
83          } else 
84          {
85              hardRefCache = null;
86          }
87      }
88  
89      public Object get(Object key)
90      {
91          Object result = null;
92          // get the SoftReference represented by that key
93          SoftReference soft_ref = (SoftReference) hash.get(key);
94          if (soft_ref != null)
95          {
96              // From the SoftReference we get the value, which can be
97              // null if it was not in the map, or it was removed in
98              // the processQueue() method defined below
99              result = soft_ref.get();
100             if (result == null)
101             {
102                 // If the value has been garbage collected, remove the
103                 // entry from the HashMap.
104                 hash.remove(key);
105             } else
106             {
107                 if (HARD_REF_SIZE>0)
108                 {
109                     // We now add this object to the beginning of the hard
110                     // reference queue.  One reference can occur more than
111                     // once, because lookups of the FIFO queue are slow, so
112                     // we don't want to search through it each time to remove
113                     // duplicates.
114 	                hardRefCache.addFirst(result);
115 	                if (hardRefCache.size() > HARD_REF_SIZE)
116 	                {
117 	                    // Remove the last entry if list longer than HARD_SIZE
118 	                    hardRefCache.removeLast();
119 	                }
120                 }
121             }
122         }
123         return result;
124     }
125 
126     /*** 
127      * Go through the ReferenceQueue and remove garbage
128      * collected SoftValue objects from the HashMap
129      */
130     private void _cleanCollectedValues()
131     {
132         SoftValue sv;
133         while ((sv = (SoftValue) queue.poll()) != null)
134         {
135             hash.remove(sv.key); // we can access private data!
136         }
137     }
138 
139     /***
140      *  Here we put the key, value pair into the HashMap using
141      *  a SoftValue object. 
142      */
143     public Object put(Object key, Object value)
144     {
145         _cleanCollectedValues(); // throw out garbage collected values first
146         return hash.put(key, new SoftValue(value, key, queue));
147     }
148 
149     /***
150      * {@inheritDoc}
151      */
152     public Object remove(Object key)
153     {
154         _cleanCollectedValues(); // throw out garbage collected values first
155         return hash.remove(key);
156     }
157 
158     /*** 
159      * {@inheritDoc}
160      */
161     public void clear()
162     {
163         if (HARD_REF_SIZE>0)
164         {
165             hardRefCache.clear();
166         }
167         _cleanCollectedValues(); // throw out garbage collected values
168         hash.clear();
169     }
170 
171     /*** 
172      * {@inheritDoc}
173      */
174     public int size()
175     {
176         _cleanCollectedValues(); // throw out garbage collected values first
177         return hash.size();
178     }
179 
180     /*** 
181      * {@inheritDoc}
182      */
183     public Set entrySet()
184     {
185         // no, no, you may NOT do that!!! GRRR
186         throw new UnsupportedOperationException();
187     }
188 }